/**
 * Created by quliang on 16-4-7 下午2:07.
 * Copyright (c) 2016 SAIZ.CN. All rights reserved.
 */

package com.example.db.utils;

import android.util.Log;

import com.example.db.exception.InstanceClassException;
import com.example.db.exception.InvokeFieldException;
import com.example.db.exception.InvokeMethodException;

import org.springframework.core.convert.TypeDescriptor;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public final class BeanUtil {
    private final static String TAG = "beanUtil";

    private BeanUtil() {
    }

    /**
     * 返回与带有给定字符串名的类或接口相关联的 Class 对象。
     *
     * @param className 所需类的完全限定名。
     * @return Class对象。
     */

    public static Class<?> getClassForName(String className) throws InstanceClassException {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            Log.w(TAG, "", e);
            throw new InstanceClassException(e);
        }
    }

    /**
     * 创建clazz类的对象实例,params是构造函数的参数
     *
     * @param clazz  class对象
     * @param params 构造参数
     * @return 该clazz的实例
     * @throws InstanceClassException 创建失败
     */

    public static <T> T instanceClass(Class<T> clazz, Object... params)
            throws InstanceClassException, InvokeMethodException {
        Class<?>[] cls = new Class<?>[params.length];
        for (int i = 0; i < cls.length; ++i) {
            cls[i] = params[i].getClass();
        }
        try {
            Constructor<T> cons = clazz.getConstructor(cls);
            return cons.newInstance(params);
        } catch (InvocationTargetException e) {
            Log.w(TAG, "", e);
            throw new InstanceClassException(e);
        } catch (NoSuchMethodException e) {
            Log.w(TAG, "", e);
            throw new InvokeMethodException(e);
        } catch (InstantiationException e) {
            Log.w(TAG, "", e);
            throw new InstanceClassException(e);
        } catch (IllegalAccessException e) {
            Log.w(TAG, "", e);
            throw new InstanceClassException(e);
        }
    }

    /**
     * 返回字段的声明类型。
     *
     * @param clazz Class 对象
     * @param name  所表示字段的字符串
     * @return Class对象
     * @throws InvokeFieldException 未发现字段表示的字符串
     */
    public static Field getField(Class<?> clazz, String name) throws InvokeFieldException {
        try {
            return clazz.getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            if ("java.lang.Object".equals(clazz.getSuperclass().getName())) {
                Log.w(TAG, "not find field:" + name, e);
                throw new InvokeFieldException(e);
            }
            return getField(clazz.getSuperclass(), name);
        }
    }

    public static TypeDescriptor getFieldDescriptor(Class<?> clazz, String name)
            throws InvokeFieldException {
        Field field = getField(clazz, name);
        return new TypeDescriptor(field);
    }

    /**
     * 返回 Field 对象的一个数组，这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
     *
     * @param clazz   Class 对象。
     * @param exclude 排除字段
     * @return Field对象数组
     */

    public static Field[] getFields(Class<?> clazz, String exclude) {
        List<Field> fs = new ArrayList<Field>();
        Class searchType = clazz.getSuperclass();
        if (!Object.class.equals(searchType) && searchType != null) {
            fs.addAll(Arrays.asList(getFields(searchType, exclude)));
        }

        Field[] all = clazz.getDeclaredFields();
        for (Field field : all) {
            String fname = field.getName();
            if (!ObjectUtil.isEmpty(exclude) && exclude.contains(fname)) {
                continue;
            }
            fs.add(field);
        }
        return fs.toArray(new Field[fs.size()]);
    }

    /**
     * 返回 Field 对象的一个数组，这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 与 getFields(clazz, "");等价
     *
     * @param clazz Class 对象。
     * @return Field 对象数组
     */
    public static Field[] getFields(Class<?> clazz) {
        return getFields(clazz, "");
    }

    /**
     * 返回字段的声明类型。
     *
     * @param clazz Class 对象
     * @param name  所表示字段的字符串
     * @return Class对象
     * @throws InvokeFieldException 未发现字段表示的字符串
     */

    public static Class<?> getFieldClass(Class<?> clazz, String name) throws InvokeFieldException {
        try {
            return clazz.getDeclaredField(name).getType();
        } catch (NoSuchFieldException e) {
            if ("java.lang.Object".equals(clazz.getSuperclass().getName())) {
                Log.w(TAG, "not find field:" + name, e);
                throw new InvokeFieldException(e);
            }
            return getFieldClass(clazz.getSuperclass(), name);
        }
    }

    public static Object getFieldValue(Object target, String fieldName) {
        ObjectUtil.assertNotEmpty(target, "this target is required; it must not be null");
        ObjectUtil.assertNotEmpty(fieldName, "this fieldName is required; it must not be null");
        Class<?> klass = target.getClass();
        try {
            Field f = getField(klass, fieldName);
            f.setAccessible(true);
            return f.get(target);
        } catch (Exception e) {
            Log.d(TAG, "", e);
            return null;
        }
    }

    public static void setFieldValue(Object target, String fieldName, Object value) {
        ObjectUtil.assertNotEmpty(target, "this target is required; it must not be null");
        ObjectUtil.assertNotEmpty(fieldName, "this fieldName is required; it must not be null");
        Class<?> klass = target.getClass();
        try {
            Field f = getField(klass, fieldName);
            f.setAccessible(true);
            f.set(target, value);
        } catch (Exception e) {
            Log.d(TAG, "", e);
        }
    }
}